/**
 * 路由跳转方法，该方法相对于直接使用uni.xxx的好处是使用更加简单快捷
 * 并且带有路由拦截功能
 */

interface RouterConfig {
    type?: string;
    url?: string;
    delta?: number;
    params?: Record<string, any>;
    animationType?: string;
    animationDuration?: number;
    intercept?: boolean;
}

declare const uni: any; // 声明uni对象，避免ts报错

class Router {
    config: RouterConfig;
    // route: (options?: string | RouterConfig, params?: Record<string, any>) => Promise<void>;

    constructor() {
        // 原始属性定义
        this.config = {
            type: 'navigateTo',
            url: '',
            delta: 1, // navigateBack页面后退时,回退的层数
            params: {}, // 传递的参数
            animationType: 'pop-in', // 窗口动画,只在APP有效
            animationDuration: 300, // 窗口动画持续时间,单位毫秒,只在APP有效
            intercept: false // 是否需要拦截
        };
        // 因为route方法是需要对外赋值给另外的对象使用，同时route内部有使用this，会导致route失去上下文
        // 这里在构造函数中进行this绑定
        this.route = this.route.bind(this);
    }

    // 判断url前面是否有"/"，如果没有则加上，否则无法跳转
    addRootPath(url: string): string {
        return url[0] === '/' ? url : `/${url}`;
    }

    // 整合路由参数
    mixinParam(url: string, params: Record<string, any>): string {
        url = url && this.addRootPath(url);
        // 使用正则匹配，主要依据是判断是否有"/","?","="等，如“/page/index/index?name=mary"
        // 如果有url中有get参数，转换后无需带上"?"
        let query = '';
        if (/.*\/.*\?.*=.*/.test(url)) {
            // object对象转为get类型的参数
            query = uni.$u.queryParams(params, false);
            // 因为已有get参数,所以后面拼接的参数需要带上"&"隔开
            return url + '&' + query;
        } else {
            // 直接拼接参数，因为此处url中没有后面的query参数，也就没有"?/&"之类的符号
            query = uni.$u.queryParams(params);
            return url + query;
        }
    }

    /**
     * 路由跳转主方法
     * @param options 跳转配置或url字符串
     * @param params 跳转参数
     */
    async route(options: string | RouterConfig = {}, params: Record<string, any> = {}): Promise<void> {
        let mergeConfig: RouterConfig = {};
        if (typeof options === 'string') {
            // 如果options为字符串，则为route(url, params)的形式
            mergeConfig.url = this.mixinParam(options, params);
            mergeConfig.type = 'navigateTo';
        } else {
            mergeConfig = uni.$u.deepMerge(this.config, options);
            // 否则正常使用mergeConfig中的url和params进行拼接
            mergeConfig.url = this.mixinParam(options.url || '', options.params || {});
        }
        if (params.intercept) {
            this.config.intercept = params.intercept;
        }
        // params参数也带给拦截器
        mergeConfig.params = params;
        // 合并内外部参数
        mergeConfig = uni.$u.deepMerge(this.config, mergeConfig);
        // 判断用户是否定义了拦截器
        if (uni.$u.routeIntercept && typeof uni.$u.routeIntercept === 'function') {
            // 定一个promise，根据用户执行resolve(true)或者resolve(false)来决定是否进行路由跳转
            const isNext = await new Promise<boolean>(resolve => {
                uni.$u.routeIntercept(mergeConfig, resolve);
            });
            // 如果isNext为true，则执行路由跳转
            isNext && this.openPage(mergeConfig);
        } else {
            this.openPage(mergeConfig);
        }
    }

    // 执行路由跳转
    openPage(config: RouterConfig): void {
        const { url = '', type = '', delta = 1, animationDuration = 300 } = config;
        if (type == 'navigateTo' || type == 'to') {
            uni.navigateTo({ url, animationDuration });
        }
        if (type == 'redirectTo' || type == 'redirect') {
            uni.redirectTo({ url });
        }
        if (type == 'switchTab' || type == 'tab') {
            uni.switchTab({ url });
        }
        if (type == 'reLaunch' || type == 'launch') {
            uni.reLaunch({ url });
        }
        if (type == 'navigateBack' || type == 'back') {
            uni.navigateBack({ delta });
        }
    }
}

export default new Router().route;
